package com.sl.springmvc.swagger.config;

import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.sl.springmvc.swagger.listener.MessageQueueListener;
import com.sl.springmvc.swagger.util.SystemConstants;

@ComponentScan(basePackages = "com.sl.springmvc.swagger.dao," +
        "com.sl.springmvc.swagger.bean," +
        "com.sl.springmvc.swagger.service" +
        "com.sl.springmvc.swagger.listener")
@Configuration
@EnableCaching
@EnableTransactionManagement
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Import({ EndpointAutoConfiguration.class, HealthIndicatorAutoConfiguration.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class SpringAppConfig {

    /**
     * 配置数据库
     */
    @Bean("mysqlDataSource")
    public JndiObjectFactoryBean getHyveMysqlDataSource() {
        JndiObjectFactoryBean factory = new JndiObjectFactoryBean();
        factory.setJndiName("sl/jdbc/mysql/LOCALDB");
        return factory;
    }

    /**
     * 配置注解的事物管理器
     * @param mysqlDataSource
     * @return
     */
    @Bean("hyveTransactionManager")
    public PlatformTransactionManager getHyveTransactionManager(@Qualifier("mysqlDataSource") DataSource mysqlDataSource) {
        PlatformTransactionManager hyveTransactionManager = new DataSourceTransactionManager(mysqlDataSource);
        return hyveTransactionManager;
    }


    /**
     * 配置缓存
     * @return
     */
    @Bean
    public SimpleCacheManager getCacheManager() {
        SimpleCacheManager cacheMgr = new SimpleCacheManager();
        List<Cache> caches = new ArrayList<Cache>();
        caches.add(new ConcurrentMapCache("default"));
        caches.add(new ConcurrentMapCache("demandData"));
        cacheMgr.setCaches(caches);
        cacheMgr.initializeCaches();
        return cacheMgr;
    }

    //加入rabbitmq
    @Bean
    public CachingConnectionFactory rabbitConnectionFactory() {
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setHost(SystemConstants.HYVE_SERVER_QUEUE_HOST);
        factory.setPort(Integer.parseInt(SystemConstants.HYVE_SERVER_QUEUE_PORT));
        factory.setUsername(SystemConstants.HYVE_SERVER_QUEUE_USERNAME);
        factory.setPassword(SystemConstants.HYVE_SERVER_QUEUE_PASSWORD);
        factory.setVirtualHost(SystemConstants.HYVE_SERVER_QUEUE_VHOST);
        return factory;
    }

    /**
     * Rabbit template.
     *
     * @return the rabbit template
     */
    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
        rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
        return rabbitTemplate;
    }

    /**
     * Gets the rabbit admin.
     *
     * @return the rabbit admin
     */
    @Bean
    public RabbitAdmin getRabbitAdmin() {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitConnectionFactory());
        return rabbitAdmin;
    }

    /**
     * Message queue.
     *
     * @return the queue
     */
    @Bean
    public Queue messageQueue() {
        return new Queue(SystemConstants.HYVE_SERVER_QUEUE_NAME + "_" + SystemConstants.HYVE_SERVER_QUEUE_EXCHANGE_KEY, true);
    }

    /**
     * Message data exchange.
     *
     * @return the topic exchange
     */
    @Bean
    public TopicExchange messageDataExchange() {
        return new TopicExchange(SystemConstants.HYVE_SERVER_QUEUE_EXCHANGE, true, false);
    }

    /**
     * Message data binding.
     *
     * @return the binding
     */
    @Bean
    public Binding messageDataBinding() {
        return BindingBuilder.bind(messageQueue()).to(messageDataExchange())
                .with(SystemConstants.HYVE_SERVER_QUEUE_EXCHANGE_KEY + "_" + SystemConstants.HYVE_SERVER_QUEUE_VERSION);
    }

    /**
     * Listener container.
     *
     * @param mqListener the mq listener
     * @return the simple message listener container
     */
    @Bean
    public SimpleMessageListenerContainer listenerContainer(@Qualifier("mqListener") MessageQueueListener mqListener) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(rabbitConnectionFactory());
        container.setQueues(messageQueue());
        container.setChannelTransacted(true);
        container.setConcurrentConsumers(1);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // Set up the manual message response
        container.setMessageListener(new MessageListenerAdapter(mqListener));
        container.setRabbitAdmin(getRabbitAdmin());
        return container;
    }

    /**
     * Endpoint handler mapping.
     *
     * @param endpoints the endpoints
     * @return the endpoint handler mapping
     */
    @Bean
    public EndpointHandlerMapping endpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
        return new EndpointHandlerMapping(endpoints);
    }

    /**
     * Health mvc endpoint.
     *
     * @param delegate the delegate
     * @return the health mvc endpoint
     */
    @Bean
    public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) {
        return new HealthMvcEndpoint(delegate, false);
    }

    /**
     * Metrics end point.
     *
     * @param delegate the delegate
     * @return the endpoint mvc adapter
     */
    @Bean
    public EndpointMvcAdapter metricsEndPoint(MetricsEndpoint delegate) {
        return new EndpointMvcAdapter(delegate);
    }


    /**
     * My sql health indicator.
     *
     * @return the health indicator
     */
    @Bean
    public HealthIndicator mySQLHealthIndicator() {
        DataSourceHealthIndicator indicator = new DataSourceHealthIndicator((DataSource) getHyveMysqlDataSource().getObject());
        return indicator;
    }

    /**
     * Rabbit mq health indicator.
     *
     * @return the rabbit health indicator
     */
    @Bean
    public RabbitHealthIndicator rabbitMQHealthIndicator() {
        RabbitHealthIndicator indicator = new RabbitHealthIndicator(rabbitTemplate());
        return indicator;
    }
}
